<?php

namespace support;

/**
 * 表格图片切割工具类
 * 能够将表格图片精确地沿表格线切割成两部分，确保两部分都包含表头
 */
class ImgCut
{
    /**
 * 分割表格图片
 * 
 * @param string $sourceImagePath 源图片路径
 * @param string $outputPath1 第一部分输出路径
 * @param string $outputPath2 第二部分输出路径
 * @return bool 是否成功切割
 */
public function cutTableImage($sourceImagePath, $outputPath1, $outputPath2)
{
    // 检查图片是否存在
    if (!file_exists($sourceImagePath)) {
        return false;
    }
    
    // 获取图片信息
    $imageInfo = getimagesize($sourceImagePath);
    if ($imageInfo === false) {
        return false;
    }
    
    // 根据图片类型创建资源
    $sourceImage = $this->createImageFromFile($sourceImagePath, $imageInfo[2]);
    if (!$sourceImage) {
        return false;
    }
    
    $width = $imageInfo[0];
    $height = $imageInfo[1];
    
    // 寻找水平分割线
    $horizontalLines = $this->findHorizontalLines($sourceImage, $width, $height);
    
    if (empty($horizontalLines)) {
        return false;
    }
    
    // 识别表头区域
    $headerEndPosition = $this->identifyHeaderEndPosition($horizontalLines);
    
    if ($headerEndPosition === false) {
        return false;
    }
    
    // 找到合适的分割位置（在表头之后，尽量在剩余表格内容的中间）
    $cutPosition = $this->findOptimalCutPosition($horizontalLines, $height, $headerEndPosition);
    
    if ($cutPosition === false) {
        return false;
    }
    
    // 创建第一部分图片（包含原始表头和部分内容）
    $topImage = imagecreatetruecolor($width, $cutPosition);
    imagecopy($topImage, $sourceImage, 0, 0, 0, 0, $width, $cutPosition);
    
    // 在第一部分图片底部添加一条线
    $this->drawBottomLine($topImage, $width, $cutPosition - 1);
    
    // 创建第二部分图片（包含表头克隆和剩余内容）
    $bottomHeight = $height - $cutPosition + $headerEndPosition;
    $bottomImage = imagecreatetruecolor($width, $bottomHeight);
    
    // 先复制表头
    imagecopy($bottomImage, $sourceImage, 0, 0, 0, 0, $width, $headerEndPosition);
    // 再复制剩余内容
    imagecopy($bottomImage, $sourceImage, 0, $headerEndPosition, 0, $cutPosition, $width, $height - $cutPosition);
    
    // 保存两部分图片
    $this->saveImage($topImage, $outputPath1, $imageInfo[2]);
    $this->saveImage($bottomImage, $outputPath2, $imageInfo[2]);
    
    // 释放资源
    imagedestroy($sourceImage);
    imagedestroy($topImage);
    imagedestroy($bottomImage);
    
    return true;
}

/**
 * 在图像底部绘制一条线，使其看起来像完整表格
 * 
 * @param resource $image 图像资源
 * @param int $width 图像宽度
 * @param int $y 绘制线的Y坐标位置
 * @return void
 */
private function drawBottomLine($image, $width, $y)
{
    // 创建黑色
    $black = imagecolorallocate($image, 0, 0, 0);
    
    // 在图像底部画一条水平线
    for ($x = 0; $x < $width; $x++) {
        imagesetpixel($image, $x, $y, $black);
    }
    
    // 为了让线更明显，可以画一条2像素宽的线
    for ($x = 0; $x < $width; $x++) {
        imagesetpixel($image, $x, $y - 1, $black);
    }
}
    
    /**
     * 根据文件类型创建图像资源
     * 
     * @param string $filePath 图片文件路径
     * @param int $type 图片类型
     * @return resource|false 图像资源
     */
    private function createImageFromFile($filePath, $type)
    {
        switch ($type) {
            case IMAGETYPE_JPEG:
                return imagecreatefromjpeg($filePath);
            case IMAGETYPE_PNG:
                return imagecreatefrompng($filePath);
            case IMAGETYPE_GIF:
                return imagecreatefromgif($filePath);
            case IMAGETYPE_BMP:
                return imagecreatefrombmp($filePath);
            default:
                return false;
        }
    }
    
    /**
     * 保存图像到文件
     * 
     * @param resource $image 图像资源
     * @param string $filePath 保存路径
     * @param int $type 图片类型
     * @return bool 是否成功保存
     */
    private function saveImage($image, $filePath, $type)
    {
        switch ($type) {
            case IMAGETYPE_JPEG:
                return imagejpeg($image, $filePath, 95);
            case IMAGETYPE_PNG:
                return imagepng($image, $filePath, 9);
            case IMAGETYPE_GIF:
                return imagegif($image, $filePath);
            case IMAGETYPE_BMP:
                return imagebmp($image, $filePath);
            default:
                return false;
        }
    }
    
    /**
     * 查找水平分割线
     * 
     * @param resource $image 图像资源
     * @param int $width 图像宽度
     * @param int $height 图像高度
     * @return array 水平线的Y坐标数组及其特征信息
     */
    private function findHorizontalLines($image, $width, $height)
    {
        $lines = [];
        $minLineLength = $width * 0.7; // 最小线长度，至少占图片宽度的70%
        
        for ($y = 0; $y < $height; $y++) {
            $linePixels = 0;
            $leftEnd = $width; // 线的左端点
            $rightStart = 0;   // 线的右端点
            
            // 检查当前行是否是一条线
            for ($x = 0; $x < $width; $x++) {
                $rgb = imagecolorat($image, $x, $y);
                $r = ($rgb >> 16) & 0xFF;
                $g = ($rgb >> 8) & 0xFF;
                $b = $rgb & 0xFF;
                
                // 判断是否是线的颜色（通常是黑色或深色）
                if ($r < 100 && $g < 100 && $b < 100) {
                    $linePixels++;
                    $leftEnd = min($leftEnd, $x);
                    $rightStart = max($rightStart, $x);
                }
            }
            
            // 如果足够多的像素是线的颜色，则认为这一行是一条线
            if ($linePixels >= $minLineLength) {
                // 存储线的位置和特征信息
                $lines[] = [
                    'y' => $y,
                    'pixels' => $linePixels,
                    'leftEnd' => $leftEnd,
                    'rightStart' => $rightStart,
                    'width' => $rightStart - $leftEnd,
                    'coverage' => $linePixels / $width // 线覆盖的比例
                ];
            }
        }
        
        return $lines;
    }
    
    /**
     * 识别表头结束位置
     * 
     * @param array $horizontalLines 水平线位置数组及其特征
     * @return int|false 表头结束位置的Y坐标
     */
    private function identifyHeaderEndPosition($horizontalLines)
    {
        if (empty($horizontalLines)) {
            return false;
        }

        // 简化的线数据，只保留y坐标
        $simplifiedLines = array_column($horizontalLines, 'y');
        
        // 1. 首先分析线的垂直分布模式
        $lineGaps = [];
        for ($i = 1; $i < count($simplifiedLines); $i++) {
            $lineGaps[] = $simplifiedLines[$i] - $simplifiedLines[$i-1];
        }
        
        // 2. 检查前几条线是否形成明显的表格头部
        // 通常表头与数据区域之间会有较大间距
        if (count($lineGaps) >= 1) {
            $avgGap = array_sum($lineGaps) / count($lineGaps);
            
            for ($i = 0; $i < min(3, count($lineGaps)); $i++) {
                // 如果某个间距明显大于平均值，可能是表头与内容的分割
                if ($lineGaps[$i] > $avgGap * 1.5) {
                    // 确认此线不是页码线
                    if (!$this->isPageNumberLine($horizontalLines[$i+1])) {
                        return $simplifiedLines[$i+1];
                    }
                }
            }
        }
        
        // 3. 检查线的特征，识别表头结构
        // 获取前三条线（如果存在）
        $topLines = array_slice($horizontalLines, 0, min(3, count($horizontalLines)));
        
        // 表头通常由横跨整个表格的线组成
        foreach ($topLines as $index => $line) {
            if ($index > 0 && $line['coverage'] > 0.9) {
                // 如果是第二或第三条线，且覆盖率高，可能是表头结束线
                // 确认此线不是页码线
                if (!$this->isPageNumberLine($line)) {
                    return $line['y'];
                }
            }
        }
        
        // 4. 如果前面的方法都失败，使用默认方法
        // 默认使用第二条线（如果存在），否则使用第一条线
        if (count($simplifiedLines) >= 2) {
            // 确保第二条线不是页码线
            if (!$this->isPageNumberLine($horizontalLines[1])) {
                return $simplifiedLines[1];
            }
        }
        
        // 如果第一条线也不是页码线，则使用它作为表头结束位置
        if (!$this->isPageNumberLine($horizontalLines[0])) {
            return $simplifiedLines[0];
        }
        
        // 如果所有前几条线都像是页码线，尝试找到第一条不是页码线的线
        foreach ($horizontalLines as $line) {
            if (!$this->isPageNumberLine($line)) {
                return $line['y'];
            }
        }
        
        // 实在没办法，使用第一条线
        return $simplifiedLines[0];
    }

    /**
     * 判断一条线是否可能是页码线
     * 
     * @param array $line 线的特征信息
     * @return bool 是否是页码线
     */
    private function isPageNumberLine($line)
    {
        // 页码线通常较短，且位于页面底部或顶部
        // 这里检查线的宽度是否较短（比如少于整个宽度的一半）
        if ($line['width'] < $line['pixels'] * 0.5) {
            return true;
        }
        
        // 页码线通常覆盖率较低
        if ($line['coverage'] < 0.5) {
            return true;
        }
        
        return false;
    }
    
    /**
     * 找到最佳切割位置（在表头之后，在剩余内容中间位置）
     * 
     * @param array $horizontalLines 水平线位置数组及其特征
     * @param int $height 图像总高度
     * @param int $headerEndPosition 表头结束位置
     * @return int|false 最佳切割位置
     */
    private function findOptimalCutPosition($horizontalLines, $height, $headerEndPosition)
    {
        // 提取简化的线数据（只有y坐标）
        $simplifiedLines = array_column($horizontalLines, 'y');
        
        // 过滤出表头后面的所有水平线
        $contentLines = array_filter($simplifiedLines, function($line) use ($headerEndPosition) {
            return $line > $headerEndPosition;
        });
        
        if (empty($contentLines)) {
            return false;
        }
        
        // 重新索引数组
        $contentLines = array_values($contentLines);
        
        // 如果内容部分只有一条线，无法合理分割
        if (count($contentLines) < 2) {
            return false;
        }
        
        // 计算内容部分的中间位置
        $contentMidPosition = ($contentLines[0] + end($contentLines)) / 2;
        
        // 找到最接近中间的线
        $closestLine = null;
        $minDistance = $height;
        
        foreach ($contentLines as $line) {
            $distance = abs($line - $contentMidPosition);
            if ($distance < $minDistance) {
                $minDistance = $distance;
                $closestLine = $line;
            }
        }
        
        return $closestLine;
    }
}